﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using DiscoveryLogic.Analytics.DataObjects.Visuals;
using DiscoveryLogic.Common.Utilities;
using DiscoveryLogic.Workflows.Components.Entities;
using DiscoveryLogic.Workflows.Components.Entities.Distances.Comparers;

namespace DiscoveryLogic.Analytics.DataObjects.Rules
{
    public class EntityClusterRule:RuleBase
    {
        #region props
        private Dictionary<string, double> _DescAttrWeights;
        /// <summary></summary>
        public Dictionary<string, double> DescAttrWeights
        {
            get
            {
                return this._DescAttrWeights;
            }
            set
            {
                this._DescAttrWeights = value;
            }
        }

        private Dictionary<string, double> _AssocAttrWeights;
        /// <summary></summary>
        public Dictionary<string, double> AssocAttrWeights
        {
            get
            {
                return this._AssocAttrWeights;
            }
            set
            {
                this._AssocAttrWeights = value;
            }
        }

        private Dictionary<string, IAttributeComparer> _Comparers;
        /// <summary></summary>
        public Dictionary<string, IAttributeComparer> Comparers
        {
            get
            {
                return this._Comparers;
            }
            set
            {
                this._Comparers = value;
            }
        }

        private Dictionary<string, double> _Penalties;
        /// <summary></summary>
        public Dictionary<string, double> Penalties
        {
            get
            {
                return this._Penalties;
            }
            set
            {
                this._Penalties = value;
            }
        }

        private double _PartitionThreshold;
        /// <summary></summary>
        public double PartitionThreshold
        {
            get
            {
                return this._PartitionThreshold;
            }
            set
            {
                this._PartitionThreshold = value;
            }
        }

        private double _ClusterThreshold;
        /// <summary></summary>
        public double ClusterThreshold
        {
            get
            {
                return this._ClusterThreshold;
            }
            set
            {
                this._ClusterThreshold = value;
            }
        }
        #endregion

        public EntityClusterRule(
            string ruleName, SetDataSourceType srcDataFormat, 
            EntityType entityType) 
            : base(ruleName, RuleType.Cluster, 
            srcDataFormat, entityType, entityType,
            EntityIcons.ClusterRuleIcon)
        {
            this._DescAttrWeights = new Dictionary<string, double>();
            this._AssocAttrWeights = new Dictionary<string, double>();
            this._Comparers = new Dictionary<string, IAttributeComparer>();
            this._Penalties = new Dictionary<string, double>();
            this._PartitionThreshold = 0;
            this._ClusterThreshold = 0;
        }

        #region Overrides of RuleBase

        public override void Save(ref XmlDocument xDoc, XmlNode ruleNode)
        {
            ruleNode.RemoveAll();
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "RuleName", this.RuleName);
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "RuleType", this.RuleType.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "IconPath", this.IconPath);

            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "SrcDataFormat", this.SrcDataFormat.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "SrcEntityType", this.SrcEntityType.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "TgtEntityType", this.TgtEntityType.ToString());

            XmlNode descWeightNodes = XmlDataUtil.AddElement(ref xDoc, ruleNode, "DescWeights");
            if (this.DescAttrWeights != null && this.DescAttrWeights.Count > 0)
            {
                foreach (string fieldName in this.DescAttrWeights.Keys)
                {
                    XmlNode descWeightNode = XmlDataUtil.AddElement(ref xDoc, descWeightNodes, "DescWeight");
                    XmlDataUtil.UpdateAttribute(ref xDoc, descWeightNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, descWeightNode, "Weight", this.DescAttrWeights[fieldName].ToString());
                }
            }
            XmlNode assocWeightNodes = XmlDataUtil.AddElement(ref xDoc, ruleNode, "AssocWeights");
            if (this.AssocAttrWeights != null && this.AssocAttrWeights.Count > 0)
            {
                foreach (string fieldName in this.AssocAttrWeights.Keys)
                {
                    XmlNode assocWeightNode = XmlDataUtil.AddElement(ref xDoc, assocWeightNodes, "AssocWeight");
                    XmlDataUtil.UpdateAttribute(ref xDoc, assocWeightNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, assocWeightNode, "Weight", this.AssocAttrWeights[fieldName].ToString());
                }
            }
            XmlNode compareNodes = XmlDataUtil.AddElement(ref xDoc, ruleNode, "Comparers");
            if (this.Comparers != null && this.Comparers.Count > 0)
            {
                foreach (string fieldName in this.Comparers.Keys)
                {
                    XmlNode compareNode = XmlDataUtil.AddElement(ref xDoc, compareNodes, "Comparer");
                    XmlDataUtil.UpdateAttribute(ref xDoc, compareNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, compareNode, "Comparer", this.Comparers[fieldName].GetType().FullName);
                }
            }
            XmlNode penaltyNodes = XmlDataUtil.AddElement(ref xDoc, ruleNode, "Penalties");
            if (this.Penalties != null && this.Penalties.Count > 0)
            {
                foreach (string fieldName in this.Penalties.Keys)
                {
                    XmlNode penaltyNode = XmlDataUtil.AddElement(ref xDoc, penaltyNodes, "Penalty");
                    XmlDataUtil.UpdateAttribute(ref xDoc, penaltyNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, penaltyNode, "Penalty", this.Penalties[fieldName].ToString());
                }
            }

            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "PartitionThreshold", this.PartitionThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "ClusterThreshold", this.ClusterThreshold.ToString());
        }

        public override RuleBase Read(XmlNode ruleNode)
        {
            EntityClusterRule rule = new EntityClusterRule(
                string.Empty, SetDataSourceType.XML, EntityType.Document);
            
            rule.RuleName = XmlDataUtil.GetAttributeValue(ruleNode, "RuleName", "");
            rule.RuleType =
                (RuleType)
                Enum.Parse(typeof(RuleType),
                           XmlDataUtil.GetAttributeValue(ruleNode, "RuleType", RuleType.Dedup.ToString()));
            rule.IconPath = XmlDataUtil.GetAttributeValue(ruleNode, "IconPath", "");
            rule.SrcDataFormat =
                (SetDataSourceType)
                Enum.Parse(typeof(SetDataSourceType), XmlDataUtil.GetAttributeValue(ruleNode, "SrcDataFormat", ""));
            rule.SrcEntityType =
                (EntityType)
                Enum.Parse(typeof(EntityType),
                           XmlDataUtil.GetAttributeValue(ruleNode, "SrcEntityType", EntityType.Grants.ToString()));
            rule.TgtEntityType =
                (EntityType)
                Enum.Parse(typeof(EntityType),
                           XmlDataUtil.GetAttributeValue(ruleNode, "TgtEntityType", EntityType.Grants.ToString()));

            rule.DescAttrWeights = new Dictionary<string, double>();
            XmlNodeList descWeightNodes = ruleNode.SelectNodes("DescWeights/DescWeight");
            if (descWeightNodes != null && descWeightNodes.Count > 0)
            {
                foreach (XmlNode descWeightNode in descWeightNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(descWeightNode, "FieldName", "");
                    double weight = double.Parse(XmlDataUtil.GetAttributeValue(descWeightNode, "Weight", "0"));
                    rule.DescAttrWeights.Add(fieldName, weight);
                }
            }
            rule.AssocAttrWeights = new Dictionary<string, double>();
            XmlNodeList assocWeightNodes = ruleNode.SelectNodes("AssocWeights/AssocWeight");
            if (assocWeightNodes != null && assocWeightNodes.Count > 0)
            {
                foreach (XmlNode assocWeightNode in assocWeightNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(assocWeightNode, "FieldName", "");
                    double weight = double.Parse(XmlDataUtil.GetAttributeValue(assocWeightNode, "Weight", "0"));
                    rule.AssocAttrWeights.Add(fieldName, weight);
                }
            }
            rule.Comparers = new Dictionary<string, IAttributeComparer>();
            XmlNodeList comparerNodes = ruleNode.SelectNodes("Comparers/Comparer");
            Assembly assembly = typeof(IAttributeComparer).Assembly;
            foreach (XmlNode comparerNode in comparerNodes)
            {
                string fieldName = XmlDataUtil.GetAttributeValue(comparerNode, "FieldName", "");
                string typeName = XmlDataUtil.GetAttributeValue(comparerNode, "Comparer", "");
                Type comparerType = assembly.GetType(typeName);
                if (comparerType != null)
                {
                    IAttributeComparer comparer = (IAttributeComparer) Activator.CreateInstance(comparerType);
                    rule.Comparers.Add(fieldName, comparer);
                }
            }
            rule.Penalties = new Dictionary<string, double>();
            XmlNodeList penaltyNodes = ruleNode.SelectNodes("Penalties/Penalty");
            foreach (XmlNode penaltyNode in penaltyNodes)
            {
                string fieldName = XmlDataUtil.GetAttributeValue(penaltyNode, "FieldName", "");
                double penalty = double.Parse(XmlDataUtil.GetAttributeValue(penaltyNode, "Penalty", "0"));
                rule.Penalties.Add(fieldName, penalty);
            }

            rule.PartitionThreshold = double.Parse(XmlDataUtil.GetAttributeValue(ruleNode, "PartitionThreshold", "100"));
            rule.ClusterThreshold = double.Parse(XmlDataUtil.GetAttributeValue(ruleNode, "ClusterThreshold", "100"));

            return rule;
        }

        #endregion
    }
}
